📝 Резюме · 📄 Оригинал (512 B)
https://t.me/Python_libr/3364

Обработка и отслеживание ошибок и исключений в Django

Источник: https://t.me/Python_libr/3364


Зачем нужна обработка исключений?

В веб-приложениях ошибки неизбежны. Правильная обработка исключений помогает:

  • Предотвратить краши приложения
  • Логировать ошибки для анализа
  • Показать пользователю понятные сообщения
  • Отслеживать проблемы в продакшене

Встроенный механизм Django

Django автоматически обрабатывает исключения и выводит 500 ошибки. Для кастомной обработки используйте декораторы и middleware:

from django.views.decorators.http import require_http_methods
from django.http import JsonResponse
import logging

logger = logging.getLogger(__name__)

# Базовый try-except в view
def get_user_data(request, user_id):
    try:
        user = User.objects.get(id=user_id)
        return JsonResponse({'user': user.name})
    except User.DoesNotExist:
        logger.warning(f"User {user_id} not found")
        return JsonResponse(
            {'error': 'Пользователь не найден'},
            status=404
        )
    except Exception as e:
        logger.error(f"Unexpected error: {str(e)}")
        return JsonResponse(
            {'error': 'Внутренняя ошибка сервера'},
            status=500
        )

Декоратор для отслеживания ошибок

from functools import wraps
from django.http import JsonResponse
import traceback
import logging

logger = logging.getLogger(__name__)

def track_errors(view_func):
    """Декоратор для автоматического отслеживания ошибок"""
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        try:
            return view_func(request, *args, **kwargs)
        except Exception as e:
            # Логируем ошибку с полным traceback
            logger.error(
                f"Error in {view_func.__name__}",
                exc_info=True,
                extra={
                    'method': request.method,
                    'path': request.path,
                    'user': request.user.id if request.user.is_authenticated else None
                }
            )

            # Возвращаем JSON ответ с ошибкой
            return JsonResponse(
                {
                    'error': str(e),
                    'type': type(e).__name__
                },
                status=500
            )
    return wrapper

# Использование
@track_errors
def create_product(request):
    data = request.POST
    product = Product.objects.create(
        name=data['name'],
        price=float(data['price'])
    )
    return JsonResponse({'product_id': product.id})

Базовый класс для обработки исключений

from django.views import View
from django.http import JsonResponse
from django.core.exceptions import ValidationError
import logging

logger = logging.getLogger(__name__)

class BaseAPIView(View):
    """Базовый класс с обработкой ошибок"""

    def dispatch(self, request, *args, **kwargs):
        try:
            return super().dispatch(request, *args, **kwargs)
        except ValidationError as e:
            logger.warning(f"Validation error: {e.message}")
            return JsonResponse({'error': e.message}, status=400)
        except ValueError as e:
            logger.warning(f"Value error: {str(e)}")
            return JsonResponse({'error': str(e)}, status=400)
        except Exception as e:
            logger.exception(f"Unhandled exception: {str(e)}")
            return JsonResponse(
                {'error': 'Внутренняя ошибка сервера'},
                status=500
            )

class ProductView(BaseAPIView):
    def post(self, request):
        price = float(request.POST['price'])  # Может вызвать ValueError
        product = Product.objects.create(
            name=request.POST['name'],
            price=price
        )
        return JsonResponse({'id': product.id})

Middleware для глобальной обработки

import json
import logging
from django.utils.deprecation import MiddlewareMixin
from django.http import JsonResponse

logger = logging.getLogger(__name__)

class ErrorHandlingMiddleware(MiddlewareMixin):
    """Middleware для перехвата всех ошибок"""

    def process_exception(self, request, exception):
        # Логируем исключение
        logger.error(
            f"Exception in {request.path}",
            exc_info=True,
            extra={
                'method': request.method,
                'user_id': request.user.id if request.user else None,
                'remote_addr': self.get_client_ip(request)
            }
        )

        # В development выводим детали, в production скрываем
        if settings.DEBUG:
            message = str(exception)
        else:
            message = 'Внутренняя ошибка сервера'

        return JsonResponse({'error': message}, status=500)

    @staticmethod
    def get_client_ip(request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

Диаграмма обработки исключений

graph TD
    A["Request приходит"] --> B["View обрабатывает"]
    B --> C{Возникла ошибка?}
    C -->|Нет| D["Response отправляется"]
    C -->|Да| E["Декоратор перехватывает"]
    E --> F["Логируется"]
    F --> G["Пользователю отправляется<br/>понятная ошибка"]

Лучшие практики

  1. Логируйте все: используйте Python logging для записи всех ошибок
  2. Не скрывайте детали: в development покажите полный traceback
  3. В production скройте детали: не раскрывайте техническую информацию
  4. Структурируйте логи: добавляйте контекст (user_id, path, метод)
  5. Используйте сервисы мониторинга: Sentry, DataDog для отслеживания в production
  6. Тестируйте обработку: создавайте тесты для ошибочных сценариев